﻿namespace TestTool.HttpTransport.MessageEncoding
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.ServiceModel.Channels;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Xml;
    using TestTool.HttpTransport;
    using TestTool.HttpTransport.Interfaces;

    internal class ControlledTextMessageEncoder : MessageEncoder
    {
        private ControlledTextMessageBindingElement _bindingElement;
        private string _contentType;
        private List<IChannelController> _controllers = new List<IChannelController>();
        private ControlledTextMessageEncoderFactory _factory;
        private MessageEncoder _mtomEncoder;
        private MessageEncoder _textEncoder;
        private XmlWriterSettings _writerSettings;

        public ControlledTextMessageEncoder(ControlledTextMessageEncoderFactory factory)
        {
            this.Initialize(factory);
        }

        public void AddControllers(IEnumerable<IChannelController> controllers)
        {
            this._controllers.AddRange(controllers);
        }

        private MessageEncoder GetEncoder(string contentType)
        {
            if (Regex.IsMatch(contentType, "^multipart.*"))
            {
                MtomMessageEncodingBindingElement element = new MtomMessageEncodingBindingElement(this._factory.MessageVersion, Encoding.GetEncoding(this._factory.CharSet));
                return element.CreateMessageEncoderFactory().Encoder;
            }
            return new TextMessageEncodingBindingElement(this._factory.MessageVersion, Encoding.GetEncoding(this._factory.CharSet)) { ReaderQuotas = { MaxStringContentLength = 0xffff } }.CreateMessageEncoderFactory().Encoder;
        }

        private void Initialize(ControlledTextMessageEncoderFactory factory)
        {
            this._factory = factory;
            this.InitializeEncoders(factory.MessageVersion, Encoding.GetEncoding(factory.CharSet));
        }

        private void InitializeEncoders(System.ServiceModel.Channels.MessageVersion messageVersion, Encoding encoding)
        {
            MessageEncoderFactory factory = new MtomMessageEncodingBindingElement(messageVersion, encoding).CreateMessageEncoderFactory();
            this._mtomEncoder = factory.Encoder;
            MessageEncoderFactory factory2 = new TextMessageEncodingBindingElement(messageVersion, encoding) { ReaderQuotas = { MaxStringContentLength = 0xffff } }.CreateMessageEncoderFactory();
            this._textEncoder = factory2.Encoder;
        }

        private byte[] ProcessMessage(Message message)
        {
            MemoryStream stream = new MemoryStream();
            this._textEncoder.WriteMessage(message, stream);
            byte[] destinationArray = new byte[stream.Length];
            Array.Copy(stream.GetBuffer(), destinationArray, stream.Length);
            ISoapMessageMutator mutator = null;
            foreach (IChannelController controller in this._bindingElement.Controllers)
            {
                if (controller is ISoapMessageMutator)
                {
                    mutator = (ISoapMessageMutator) controller;
                    break;
                }
            }
            if (mutator != null)
            {
                destinationArray = mutator.ProcessMessage(destinationArray);
            }
            return destinationArray;
        }

        public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
        {
            byte[] destinationArray = new byte[buffer.Count];
            Array.Copy(buffer.Array, buffer.Offset, destinationArray, 0, destinationArray.Length);
            bufferManager.ReturnBuffer(buffer.Array);
            string xml = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
            try
            {
                MemoryStream outStream = new MemoryStream();
                XmlDocument document = new XmlDocument();
                document.LoadXml(xml);
                document.Save(outStream);
                outStream.Seek(0L, SeekOrigin.Begin);
                TextReader reader = new StreamReader(outStream);
                xml = reader.ReadToEnd();
                reader.Close();
            }
            catch (Exception)
            {
            }
            foreach (IChannelController controller in this._controllers)
            {
                ITrafficListener listener = controller as ITrafficListener;
                if (listener != null)
                {
                    listener.LogResponse(xml);
                }
            }
            if (contentType == "application/soap+xml")
            {
                MemoryStream stream = new MemoryStream(destinationArray);
                foreach (IChannelController controller2 in this._controllers)
                {
                    IValidatingController controller3 = controller2 as IValidatingController;
                    if (controller3 != null)
                    {
                        controller3.Validate(stream);
                    }
                }
                stream.Close();
            }
            MemoryStream stream3 = new MemoryStream(destinationArray);
            return this.ReadMessage(stream3, 0x7fffffff, contentType);
        }

        public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
        {
            return this.GetEncoder(contentType).ReadMessage(stream, maxSizeOfHeaders);
        }

        public void SetBindingElement(ControlledTextMessageBindingElement bindingElement)
        {
            this._bindingElement = bindingElement;
        }

        public override void WriteMessage(Message message, Stream stream)
        {
            XmlWriter writer = XmlWriter.Create(stream, this._writerSettings);
            message.WriteMessage(writer);
            writer.Close();
        }

        public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
        {
            int headerIndex = -1;
            for (int i = 0; i < message.Headers.Count; i++)
            {
                if (message.Headers[i].Name == "VsDebuggerCausalityData")
                {
                    headerIndex = i;
                    break;
                }
            }
            if (headerIndex >= 0)
            {
                message.Headers.RemoveAt(headerIndex);
            }
            byte[] sourceArray = this.ProcessMessage(message);
            int length = sourceArray.Length;
            int bufferSize = length + messageOffset;
            byte[] destinationArray = bufferManager.TakeBuffer(bufferSize);
            Array.Copy(sourceArray, 0, destinationArray, messageOffset, length);
            ArraySegment<byte> segment = new ArraySegment<byte>(destinationArray, messageOffset, length);
            string xml = Encoding.GetEncoding(this._factory.CharSet).GetString(sourceArray);
            try
            {
                MemoryStream outStream = new MemoryStream();
                XmlDocument document = new XmlDocument();
                document.LoadXml(xml);
                document.Save(outStream);
                outStream.Seek(0L, SeekOrigin.Begin);
                TextReader reader = new StreamReader(outStream);
                xml = reader.ReadToEnd();
                reader.Close();
            }
            catch (Exception)
            {
            }
            foreach (IChannelController controller in this._controllers)
            {
                ITrafficListener listener = controller as ITrafficListener;
                if (listener != null)
                {
                    listener.LogRequest(xml);
                }
            }
            return segment;
        }

        public override string ContentType
        {
            get
            {
                return this._contentType;
            }
        }

        public override string MediaType
        {
            get
            {
                return this._factory.MediaType;
            }
        }

        public override System.ServiceModel.Channels.MessageVersion MessageVersion
        {
            get
            {
                return this._factory.MessageVersion;
            }
        }
    }
}

